package org.hepeng.workx.mybatis.mapper;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.hepeng.workx.util.ClassUtils;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author he peng
 */

@Setter
@Getter
public class TableMapMetadata {

    private String tableName;
    private Set<String> allColumnNames;
    private Map<String , String> columnToFieldMap;
    private Map<String , String> identityColumnToFieldMap;
    private Set<String> identityColumnNames;
    private Set<String> identityFieldNames;
    private Class<?> mapperClass;
    private Class<?> entityClass;

    private TableMapMetadata() {}

    public static TableMapMetadata parse(Class<?> mapperClass) {
        Assert.notNull(mapperClass , "mapperClass must not be null");

        Class<?>[] interfaces = mapperClass.getInterfaces();
        if (Objects.isNull(interfaces) ||
                ! ArrayUtils.contains(interfaces , Mapper.class)) {
            return null;
        }
        Class<?> entityClass = ClassUtils.getSingleGeneric(mapperClass , Mapper.class);
        /*SimplifyCodingSupport simplifyCodingSupport = mapperClass.getAnnotation(SimplifyCodingSupport.class);
        if (Objects.isNull(simplifyCodingSupport)) {
            return null;
        }

        Class<?> entityClass = simplifyCodingSupport.entityClass();*/
        TableMap tableMap = entityClass.getAnnotation(TableMap.class);
        if (Objects.isNull(tableMap)) {
            throw new IllegalStateException("must be annotated by @" + TableMap.class);
        }

        TableMapMetadata tableMapMetaData = new TableMapMetadata();
        tableMapMetaData.setEntityClass(entityClass);
        tableMapMetaData.setMapperClass(mapperClass);
        tableMapMetaData.setTableName(tableMap.tableName());
        List<Field> idFields = FieldUtils.getFieldsListWithAnnotation(entityClass, Id.class);
        if (CollectionUtils.isNotEmpty(idFields)) {
            Set<String> identityColumnNames = new HashSet<>();
            Set<String> identityFieldNames = new HashSet<>();
            Map<String , String> identityColumnToFieldMap = new HashMap<>();
            for (Field idField : idFields) {
                Id idAnnotation = idField.getAnnotation(Id.class);
                String identityColumnName = idAnnotation.value();
                identityColumnNames.add(identityColumnName);
                identityFieldNames.add(idField.getName());
                identityColumnToFieldMap.put(identityColumnName , idField.getName());
            }
            tableMapMetaData.setIdentityColumnNames(identityColumnNames);
            tableMapMetaData.setIdentityFieldNames(identityFieldNames);
            tableMapMetaData.setIdentityColumnToFieldMap(identityColumnToFieldMap);
        }

        List<Field> allFields = FieldUtils.getAllFieldsList(entityClass);
        List<Field> columnFields = FieldUtils.getFieldsListWithAnnotation(entityClass, Column.class);
        List<Field> othersFields = ListUtils.subtract(allFields, columnFields);

        Map<String , String> columnToFieldMap = new HashMap<>();
        Set<String> allColumnNames = new HashSet<>();
        if (CollectionUtils.isNotEmpty(columnFields)) {
            for (Field columnField : columnFields) {
                Column columnAnnotation = columnField.getAnnotation(Column.class);
                allColumnNames.add(columnAnnotation.value());
                columnToFieldMap.put(columnAnnotation.value() , columnField.getName());
            }
            allColumnNames.addAll(tableMapMetaData.getIdentityColumnNames());
            columnToFieldMap.putAll(tableMapMetaData.getIdentityColumnToFieldMap());
        } else {
            for (Field field : allFields) {
                allColumnNames.add(field.getName());
                columnToFieldMap.put(field.getName() , field.getName());
            }
        }

//        if (CollectionUtils.isNotEmpty(othersFields)) {
//            for (Field field : othersFields) {
//                if (ClassUtils.isPrimitiveOrWrapper(field.getType())) {
//                    allColumnNames.add(field.getName());
//                    columnToFieldMap.put(field.getName() , field.getName());
//                }
//            }
//        }

//        TODO WorkXMybatisEnvironment 是否遵守驼峰命名法

        tableMapMetaData.setAllColumnNames(allColumnNames);
        tableMapMetaData.setColumnToFieldMap(columnToFieldMap);
        return tableMapMetaData;
    }
}
